package com.ls.task.utils;

import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.joda.time.DateTimeUtils;
import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.ScheduleBuilder;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;

import com.ls.commons.schedule.exception.MyScheduleException;
import com.ls.fw.commons.log.exception.ExceptionsHelper;
import com.ls.fw.commons.spring.SpringContextHolder;
import com.ls.task.dal.model.SysTask;
import com.ls.task.dal.model.SysTaskTrigger;
import com.ls.task.job.LinkJob;
import com.ls.task.support.schedule.quartz.MethodInvokingJobDetailFactoryBean;

public class TaskUtils {

	public static final String TASK_ID = "taskId";
	public static final String TRIGGER_ID = "triggerId";
	private static final Logger logger = LoggerFactory.getLogger(TaskUtils.class);
	/**
	 * 校验Corn表达式
	 * 
	 * @param corn
	 * @return boolean
	 */
	public static boolean validateCornExpression(String corn) {
		return CronExpression.isValidExpression(corn);
	}

	public static JobDetail loadTask(SysTask sysTask){
		return loadTask((ConfigurableApplicationContext)SpringContextHolder.getApplicationContext(),sysTask);
	}
	public static JobDetail loadTask(ConfigurableApplicationContext configurableApplicationContext
			,SysTask sysTask){
		JobDetail jobDetail = null;
		String jobId = sysTask.getSpringId();
		if(StringUtils.isBlank(jobId)){
			jobId = LinkJob.class.getSimpleName()+sysTask.getId();
		} 
		String taskSpringId = jobId+""+sysTask.getId();  
		if(!SpringContextHolder.containsBean(taskSpringId)) {
			 // 获取bean工厂并转换为DefaultListableBeanFactory  
			 DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext  
					 .getBeanFactory();  
			 
			 // 通过BeanDefinitionBuilder创建bean定义  
			 BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder  
					 .genericBeanDefinition(MethodInvokingJobDetailFactoryBean.class);  
			 String targetMethod = sysTask.getMethodName();
			 if(Boolean.TRUE.equals(sysTask.getUrlClass())){
				 String jobClass = sysTask.getJobClass();
				 if(StringUtils.isBlank(jobClass)){
					 jobClass = LinkJob.class.getName();
				 }
				 try {
					loanBean((ConfigurableApplicationContext)SpringContextHolder.getApplicationContext(), jobId, sysTask.getUrl(),Class.forName(jobClass));
				 } catch (ClassNotFoundException e) {
					throw new IllegalArgumentException(ExceptionsHelper.getRootCause(e).getMessage(),e);
				 }
				 if(StringUtils.isBlank(targetMethod)){
					 targetMethod = LinkJob.METHODNAME;
				 } 
			 }else{
				 check(jobId, sysTask.getJobClass());
			 }
			 beanDefinitionBuilder.addPropertyReference("targetObject", jobId);
			 beanDefinitionBuilder.addPropertyValue("targetMethod", targetMethod);
			 beanDefinitionBuilder.addPropertyValue("name", getTaskName(sysTask));
			 beanDefinitionBuilder.addPropertyValue("group", getTaskGroupName(sysTask));
			 beanDefinitionBuilder.addPropertyValue("concurrent", sysTask.getConcurrent());
			 
			 // 注册bean  
			 defaultListableBeanFactory.registerBeanDefinition(taskSpringId,  
					 beanDefinitionBuilder.getRawBeanDefinition()); 
			 logger.info("spring 动态加载{}({})",taskSpringId,sysTask.getJobClass()+"jobDetail");
		 }
		 jobDetail = SpringContextHolder.getBean(taskSpringId);
         JobDataMap newJobDataMap = new JobDataMap();
         newJobDataMap.put(TASK_ID, sysTask.getId());
		 jobDetail.getJobDataMap().putAll(newJobDataMap);
		 return jobDetail;
	}

	public static void loanBean(ConfigurableApplicationContext configurableApplicationContext
			, Class<?> clas, String springId ){
		// 获取bean工厂并转换为DefaultListableBeanFactory  
		DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext  
		        .getBeanFactory();  
		  
		// 通过BeanDefinitionBuilder创建bean定义  
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder  
		        .genericBeanDefinition(clas);  
//		if(!singlon){
//			beanDefinitionBuilder.setScope(BeanDefinition.SCOPE_PROTOTYPE);
//		}
		// 注册bean  
		defaultListableBeanFactory.registerBeanDefinition(springId,  
		        beanDefinitionBuilder.getRawBeanDefinition()); 
		logger.info("spring 动态加载{}({})",springId,clas.getName());
	}

	public static void loanBean(ConfigurableApplicationContext configurableApplicationContext
			, String springId , String url, Class<?> jobClass){
		// 获取bean工厂并转换为DefaultListableBeanFactory  
		DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext  
		        .getBeanFactory();  
		// 通过BeanDefinitionBuilder创建bean定义  
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder  
		        .genericBeanDefinition(jobClass);  
		beanDefinitionBuilder.addPropertyValue("url", url);
		// 注册bean  
		defaultListableBeanFactory.registerBeanDefinition(springId,  
		        beanDefinitionBuilder.getRawBeanDefinition()); 
		logger.info("spring 动态加载{}({})",springId,jobClass.getName());
	}
	public static Object check(String springId, String target){
		Object object = null;
        if(SpringContextHolder.containsBean(springId)) {
            object = SpringContextHolder.getBean(springId);
        }else {
            try {
				object = SpringContextHolder.getBean(Class.forName(target));
			} catch (Exception e) {
			}
        }
        if(object == null) {
            logger.warn(springId+"从Spring容器中无法获取目标执行类!");
            try {
				loanBean((ConfigurableApplicationContext)SpringContextHolder.getApplicationContext(), Class.forName(target), springId);
			} catch (ClassNotFoundException e) {
				throw new IllegalArgumentException(ExceptionsHelper.getRootCause(e).getMessage(),e);
			}
        }
        return object;
	}

	
    public static TriggerKey triggerKey(SysTaskTrigger sysTaskTrigger,SysTask  sysTask) {
        return TriggerKey.triggerKey(getTriggerName(sysTaskTrigger, sysTask), getTriggerGroupName(sysTaskTrigger, sysTask));
    }
    
    public static TriggerKey triggerKey(String triggerName, String triggerGroup) {
        return TriggerKey.triggerKey(triggerName, triggerGroup);
    }
    
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static Trigger trigger(Trigger oldt,SysTaskTrigger sysTaskTrigger,SysTask  sysTask) {
        TriggerBuilder tb = null;
        if(oldt == null) {
            tb = TriggerBuilder.newTrigger().withIdentity(triggerKey(sysTaskTrigger,sysTask))
            		.withSchedule(buildSchedule(
            				sysTaskTrigger.getCategoryCode(),sysTaskTrigger.getExpression()));
        }else {
            tb =  oldt.getTriggerBuilder()
            		.withIdentity(triggerKey(sysTaskTrigger,sysTask))
            		.withSchedule(buildSchedule(sysTaskTrigger.getCategoryCode(),sysTaskTrigger.getExpression()));
        }
        if(sysTask != null) {
            tb.forJob(jobKey(sysTask));
        }
        if(sysTaskTrigger.getLevel()!=null) {
            tb.withPriority(sysTaskTrigger.getLevel());
        }
        if(sysTaskTrigger.getStartDt()!=null && sysTask.getStartDt()!=null){
        	 Calendar oldCal = Calendar.getInstance();
        	 oldCal.setTime(sysTask.getStartDt());

        	 Calendar newCal = Calendar.getInstance();
    	     newCal.setTime(sysTaskTrigger.getStartDt());
    	     
    	     if (oldCal.before(newCal)) {
    	    	 tb.endAt(newCal.getTime());
    	     }
    	     if (oldCal.equals(newCal)) {
    	    	 tb.endAt(newCal.getTime());
    	     }
    	     if (oldCal.after(newCal)) {
    	    	 tb.endAt(oldCal.getTime());
    	     }
        }else{
        	if(sysTask.getStartDt()!=null) {
        		tb.startAt(sysTask.getStartDt());
        	}
        	if(sysTaskTrigger.getStartDt()!=null) {
        		tb.startAt(sysTaskTrigger.getStartDt());
        	}
        }
        if(sysTaskTrigger.getEndDt()!=null && sysTask.getEndDt()!=null){
        	 Calendar oldCal = Calendar.getInstance();
        	 oldCal.setTime(sysTask.getEndDt());

        	 Calendar newCal = Calendar.getInstance();
    	     newCal.setTime(sysTaskTrigger.getEndDt());
    	     
    	     if (oldCal.before(newCal)) {
    	    	 tb.endAt(newCal.getTime());
    	     }
    	     if (oldCal.equals(newCal)) {
    	    	 tb.endAt(newCal.getTime());
    	     }
    	     if (oldCal.after(newCal)) {
    	    	 tb.endAt(oldCal.getTime());
    	     }
        }else{
        	if(sysTask.getEndDt()!=null) {
        		tb.endAt(sysTask.getEndDt());
        	}
        	if(sysTaskTrigger.getEndDt()!=null) {
        		tb.endAt(sysTaskTrigger.getEndDt());
        	}
        }
        JobDataMap newJobDataMap = new JobDataMap();
        newJobDataMap.put(TRIGGER_ID, sysTaskTrigger.getId());
        newJobDataMap.put(TASK_ID, sysTask.getId());
		tb.usingJobData(newJobDataMap );
        return tb.build();
    }
    
    @SuppressWarnings({ "rawtypes" })
    public static Trigger trigger(SysTask  sysTask) {
        TriggerBuilder tb = null;
        tb = TriggerBuilder.newTrigger().withIdentity(triggerKey(null,sysTask))
        		.withSchedule(SimpleScheduleBuilder.simpleSchedule().withRepeatCount(0).withIntervalInSeconds(1));
        if(sysTask != null) {
            tb.forJob(jobKey(sysTask));
        }
        JobDataMap newJobDataMap = new JobDataMap();
        newJobDataMap.put(TASK_ID, sysTask.getId());
		tb.usingJobData(newJobDataMap );
        return tb.build();
    }
    public static JobKey jobKey(SysTask  sysTask) {
        return JobKey.jobKey(TaskUtils.getTaskName(sysTask), TaskUtils.getTaskGroupName(sysTask));
    }
    public static JobKey jobKey(String jobName, String jobGroup) {
        return JobKey.jobKey(jobName, jobGroup);
    }
    public static String getTriggerName(SysTaskTrigger trigger){
    	StringBuilder sb = new StringBuilder();
    	if(StringUtils.isNotBlank(trigger.getNameEn())){
    		sb.append(trigger.getNameEn());
    	}else{
    		sb.append(trigger.getId()+"");
    	}
    	return sb.toString();
    }

    public static String getTriggerGroupName(SysTaskTrigger trigger){
    	StringBuilder sb = new StringBuilder();
    	if(StringUtils.isNotBlank(trigger.getGroupName())){
    		sb.append(trigger.getGroupName());
    	}else{
    		if(trigger.getTaskId()!=null){
    			sb.append(trigger.getTaskId()+"");
    		}else{
    			sb.append(trigger.getId()+"");
    		}
    	}
    	return sb.toString();
    }

    public static String getTriggerName(SysTaskTrigger trigger,SysTask sysTask){
    	StringBuilder sb = new StringBuilder();
    	if(trigger != null){
    		if(StringUtils.isNotBlank(trigger.getNameEn())){
    			sb.append(trigger.getId()+"#"+sysTask.getId()+"#"+trigger.getNameEn());
    		}else{
    			if(StringUtils.isNotBlank(sysTask.getNameEn())){
    				sb.append(trigger.getId()+"#"+sysTask.getId()+"#"+sysTask.getNameEn());
    			}else{
    				sb.append(trigger.getId()+"#"+sysTask.getId());
    			}
    		}
    	}else{
    		sb.append(""+"#"+sysTask.getId());
    	}
    	return sb.toString();
    }

    public static String getTriggerGroupName(SysTaskTrigger trigger,SysTask sysTask){
    	StringBuilder sb = new StringBuilder();
    	if(trigger != null){
	    	if(StringUtils.isNotBlank(trigger.getGroupName())){
	    		sb.append(trigger.getGroupName());
	    	}else{
	    		if(StringUtils.isNotBlank(sysTask.getGroupName())){
	        		sb.append(sysTask.getGroupName()+trigger.getId());
	        	}else{
	        		sb.append(trigger.getId()+"");
	        	}
	    	}
    	}else{
    		sb.append(""+"#"+sysTask.getId());
    	}
    	return sb.toString();
    }

    public static String getTaskName(SysTask sysTask){
    	StringBuilder sb = new StringBuilder();
    	if(StringUtils.isNotBlank(sysTask.getNameEn())){
    		sb.append(sysTask.getId()+"#"+sysTask.getNameEn());
    	}else{
        	sb.append(sysTask.getId()+"");
    	}
    	return sb.toString();
    }

    public static String getTaskGroupName(SysTask sysTask){
    	StringBuilder sb = new StringBuilder();
    	if(StringUtils.isNotBlank(sysTask.getGroupName())){
    		sb.append(sysTask.getGroupName());
    	}else{
            sb.append(sysTask.getId()+"");
    	}
    	return sb.toString();
    }

    @SuppressWarnings("rawtypes")
	public static ScheduleBuilder buildSchedule(String type,String expression) {
    	return buildSchedule(type, expression,-1);
    }
    
    @SuppressWarnings("rawtypes")
    public static ScheduleBuilder buildSchedule(String type,String expression,int repeatCount) {
        ScheduleBuilder sb = null;
        if("corn".equals(type)) {
            if(!CronExpression.isValidExpression(expression)) {
                throw new MyScheduleException("ScheduleTrigger***配置信息***定时Corn表达式格式错误!!!");
            }
            sb = CronScheduleBuilder.cronSchedule(expression);
        }else {
            String subType = type.split("-")[1];
            String[] values = expression.trim().split("/");
            SimpleScheduleBuilder ssb = null;
            switch(subType) {
            case "S":
                ssb = SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(Integer.parseInt(values[0]));
                break;
            case "MI":
                ssb = SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInMinutes(Integer.parseInt(values[0]));
                
                break;
            case "H":
                ssb = SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInHours(Integer.parseInt(values[0]));
                break;
            case "D":
                sb = CronScheduleBuilder.dailyAtHourAndMinute(
                        Integer.parseInt(values[0]),
                        Integer.parseInt(values[1]));
                break;
            case "W":
                sb = CronScheduleBuilder.weeklyOnDayAndHourAndMinute(
                        Integer.parseInt(values[0]),
                        Integer.parseInt(values[1]),
                        Integer.parseInt(values[2]));
                break;
            case "M":
                sb = CronScheduleBuilder.monthlyOnDayAndHourAndMinute(
                        Integer.parseInt(values[0]),
                        Integer.parseInt(values[1]),
                        Integer.parseInt(values[2]));
                break;
            }
            if(ssb != null) {
                if(values[1].equalsIgnoreCase("N")) {
                    ssb.repeatForever();
                }else {
                    ssb.withRepeatCount(Integer.parseInt(values[1]));
                }
                sb = ssb;
            }
        }
        return sb;
    }
}
